home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 037a / exec31.zip / EXEC.PAS < prev    next >
Pascal/Delphi Source File  |  1991-08-18  |  29KB  |  1,011 lines

  1. Unit exec;
  2. {  --- Version 3.1 91-08-17 23:08 ---
  3.  
  4.    EXEC.PAS: EXEC function with memory swap - prepare parameters.
  5.  
  6.    Needs Assembler file 'spawn.asm' (assembled as 'spawnp.obj')
  7.    and unit 'checkpat'.
  8.  
  9. Public domain software by
  10.  
  11.         Thomas Wagner
  12.         Ferrari electronic GmbH
  13.         Beusselstrasse 27
  14.         D-1000 Berlin 21
  15.         West Germany
  16.  
  17.         BIXname: twagner
  18. }
  19.  
  20. Interface
  21.  
  22. Uses
  23.   Dos, checkpat;
  24.  
  25. const
  26.  
  27. {e Return codes (only upper byte significant) }
  28. {d Fehlercodes (nur das obere Byte signifikant) }
  29.  
  30.    RC_PREPERR   = $0100;
  31.    RC_NOFILE    = $0200;
  32.    RC_EXECERR   = $0300;
  33.    RC_ENVERR    = $0400;
  34.    RC_SWAPERR   = $0500;
  35.    RC_REDIRERR  = $0600;
  36.  
  37. {e Swap method and option flags }
  38. {d Auslagerungsmethoden ond Optionen }
  39.  
  40.    USE_EMS      =  $01;
  41.    USE_XMS      =  $02;
  42.    USE_FILE     =  $04;
  43.    EMS_FIRST    =  $00;
  44.    XMS_FIRST    =  $10;
  45.    HIDE_FILE    =  $40;
  46.    NO_PREALLOC  = $100;
  47.    CHECK_NET    = $200;
  48.  
  49.    USE_ALL      = USE_EMS or USE_XMS or USE_FILE or CHECK_NET;
  50.  
  51.  
  52. type
  53.     filename = string [81];
  54.     string128 = string [128];
  55.     pstring = ^string;
  56.  
  57.  
  58. function do_exec (xfn: string; pars: string; spawn: integer;
  59.                   needed: word; newenv: boolean): integer;
  60.  
  61.    {>e
  62.       The EXEC function.
  63.  
  64.       Parameters:
  65.  
  66.          xfn      is a string containing the name of the file
  67.                   to be executed. If the string is empty,
  68.                   the COMSPEC environment variable is used to
  69.                   load a copy of COMMAND.COM or its equivalent.
  70.                   If the filename does not include a path, the
  71.                   current PATH is searched after the default.
  72.                   If the filename does not include an extension,
  73.                   the path is scanned for a COM, EXE, or BAT file 
  74.                   in that order.
  75.  
  76.          pars     The program parameters.
  77.  
  78.          spawn    If 0, the function will terminate after the 
  79.                   EXECed program returns, the function will not return.
  80.  
  81.                   NOTE: If the program file is not found, the function
  82.                         will always return with the appropriate error 
  83.                         code, even if 'spawn' is 0.
  84.  
  85.                   If non-0, the function will return after executing the
  86.                   program. If necessary (see the "needed" parameter),
  87.                   memory will be swapped out before executing the program.
  88.                   For swapping, spawn must contain a combination of the
  89.                   following flags:
  90.  
  91.                      USE_EMS  ($01)  - allow EMS swap
  92.                      USE_XMS  ($02)  - allow XMS swap
  93.                      USE_FILE ($04)  - allow File swap
  94.  
  95.                   The order of trying the different swap methods can be
  96.                   controlled with one of the flags
  97.  
  98.                      EMS_FIRST ($00) - EMS, XMS, File (default)
  99.                      XMS_FIRST ($10) - XMS, EMS, File
  100.  
  101.                   If swapping is to File, the attribute of the swap file
  102.                   can be set to "hidden", so users are not irritated by
  103.                   strange files appearing out of nowhere with the flag
  104.  
  105.                      HIDE_FILE ($40) - create swap file as hidden
  106.  
  107.                   and the behaviour on Network drives can be changed with
  108.  
  109.                      NO_PREALLOC (0x100) - don't preallocate
  110.                      CHECK_NET (0x200)   - don't preallocate if file on net.
  111.  
  112.                   This checking for Network is mainly to compensate for
  113.                   a strange slowdown on Novell networks when preallocating
  114.                   a file. You can either set NO_PREALLOC to avoid allocation
  115.                   in any case, or let the prep_swap routine decide whether
  116.                   to do preallocation or not depending on the file being
  117.                   on a network drive (this will only work with DOS 3.1 or 
  118.                   later).
  119.  
  120.          needed   The memory needed for the program in paragraphs (16 Bytes).
  121.                   If not enough memory is free, the program will
  122.                   be swapped out.
  123.                   Use 0 to never swap, $ffff to always swap. 
  124.                   If 'spawn' is 0, this parameter is irrelevant.
  125.  
  126.          newenv   If this parameter is FALSE, the environment
  127.                   of the spawned program is a copy of the parent's
  128.                   environment. If it is TRUE, a new environment
  129.                   is created which includes the modifications from
  130.                   previous 'putenv' calls.
  131.  
  132.       Return value:
  133.  
  134.          $0000..00FF: The EXECed Program's return code
  135.  
  136.          $0101:       Error preparing for swap: no space for swapping
  137.          $0102:       Error preparing for swap: program too low in memory
  138.  
  139.          $0200:       Program file not found
  140.          $0201:       Program file: Invalid drive
  141.          $0202:       Program file: Invalid path
  142.          $0203:       Program file: Invalid name
  143.          $0204:       Program file: Invalid drive letter
  144.          $0205:       Program file: Path too long
  145.          $0206:       Program file: Drive not ready
  146.          $0207:       Batchfile/COMMAND: COMMAND.COM not found
  147.          $0208:       Error allocating temporary buffer
  148.  
  149.          $03xx:       DOS-error-code xx calling EXEC
  150.  
  151.          $0400:       Error allocating environment buffer
  152.  
  153.          $0500:       Swapping requested, but prep_swap has not 
  154.                        been called or returned an error.
  155.          $0501:       MCBs don't match expected setup
  156.          $0502:       Error while swapping out
  157.  
  158.          $0600:       Redirection syntax error
  159.          $06xx:       DOS error xx on redirection
  160.    <}
  161.  
  162.    {>d
  163.       Die EXEC Funktion.
  164.  
  165.       Parameter:
  166.  
  167.          xfn      ist ein String mit dem Namen der auszuführenden Datei.
  168.                   Ist der String leer, wird die COMSPEC Umgebungsvariable
  169.                   benutzt um COMMAND.COM oder das Equivalent zu laden.
  170.                   Ist kein Pfad angegeben, wird nach dem aktuellen Pfad
  171.                   der in der PATH Umgebungsvariablen angegebene Pfad
  172.                   durchsucht.
  173.                   Ist kein Dateityp angegeben, wird der Pfad nach
  174.                   einer COM oder EXE Datei (in dieser Reihenfolge) abgesucht.
  175.  
  176.          pars     Die Kommandozeile
  177.  
  178.          spawn    Wenn 0, wird der Programmlauf beendet wenn das
  179.                   aufgerufene Programm zurückkehrt, die Funktion kehrt
  180.                   nicht zurück.
  181.  
  182.                   HINWEIS: Wenn die auszuführende Datei nicht gefunden
  183.                         wird, kehrt die Funktion mit einem Fehlercode
  184.                         zurück, auch wenn der 'spawn' Parameter 0 ist.
  185.  
  186.                   Wenn nicht 0, kehrt die Funktion nach Ausführung des
  187.                   Programms zurück. Falls notwendig (siehe den Parameter
  188.                   "needed") wird der Programmspeicherbereich vor Aufruf
  189.                   ausgelagert.
  190.                   Zur Auslagerung muß der Parameter eine Kombination der
  191.                   folgenden Flags enthalten:
  192.  
  193.                      USE_EMS  ($01)  - Auslagerung auf EMS zulassen
  194.                      USE_XMS  ($02)  - Auslagerung auf XMS zulassen
  195.                      USE_FILE ($04)  - Auslagerung auf Datei zulassen
  196.  
  197.                   Die Reihenfolge der Versuche, auf die verschiedenen
  198.                   Medien auszulagern kann mit einem der folgenden
  199.                   Flags beeinflußt werden:
  200.  
  201.                      EMS_FIRST ($00) - EMS, XMS, Datei (Standard)
  202.                      XMS_FIRST ($10) - XMS, EMS, Datei
  203.  
  204.                   Wenn die Auslagerung auf Datei erfolgt, kann das
  205.                   Attribut dieser Datei auf "hidden" gesetzt werden,
  206.                   damit der Benutzer nicht durch unversehends auftauchende
  207.                   Dateien verwirrt wird:
  208.  
  209.                      HIDE_FILE ($40) - Auslagerungsdatei "hidden" erzeugen
  210.  
  211.                   Außerdem kann das Verhalten auf Netzwerk-Laufwerken 
  212.                   beeinflußt werden mit
  213.  
  214.                      NO_PREALLOC (0x100) - nicht Präallozieren
  215.                      CHECK_NET (0x200)   - nicht Präallozieren wenn Netz.
  216.  
  217.                   Diese Prüfung auf Netzwerk ist hauptsächlich sinnvoll
  218.                   für Novell Netze, bei denen eine Präallozierung eine
  219.                   erhebliche Verzögerung bewirkt. Sie können entweder mit
  220.                   NO_PREALLOC eine Präallozierung in jedem Fall ausschließen,
  221.                   oder die Entscheidung mit CHECK_NET prep_swap überlassen.
  222.                   In diesem Fall wird nicht präalloziert wenn die Datei
  223.                   auf einem Netzwerk-Laufwerk liegt (funktioniert nur
  224.                   mit DOS Version 3.1 und späteren).
  225.  
  226.          needed   Der zur Ausführung des Programms benötigte Speicher
  227.                   in Paragraphen (16 Bytes). Wenn nicht ausreichend 
  228.                   freier Speicher vorhanden ist, wird der Programm-
  229.                   speicherbereich ausgelagert.
  230.                   Bei Angabe von 0 wird nie ausgelagert, bei Angabe
  231.                   von $ffff wird immer ausgelagert.
  232.                   Ist der Parameter 'spawn' 0, hat 'needed' keine Bedeutung.
  233.  
  234.          newenv   Bestimmt die dem gerufenen Programm zu übergebenden 
  235.                   Umgebungsvariablen. Ist der Parameter FALSE,
  236.                   wird eine Kopie der Vater-Umgebung benutzt,
  237.                   d.h. daß Aufrufe von "putenv" keinen Effekt haben.
  238.                   Ist er TRUE, wird eine neue Umgebung mit den 
  239.                   Modifikationen aus 'putenv' übergeben.
  240.  
  241.       Liefert:
  242.  
  243.          $0000..00FF: Rückgabewert des aufgerufenen Programms
  244.  
  245.          $0101:       Fehler bei Vorbereitung zum Auslagern -
  246.                        kein Speicherplatz in XMS/EMS/Datei
  247.          $0102:       Fehler bei Vorbereitung zum Auslagern -
  248.                        der Programmcode ist zu nah am Beginn des
  249.                        Programms.
  250.  
  251.          $0200:       Auszuführende Programmdatei nicht gefunden
  252.          $0201:       Programmdatei: Ungültiges Laufwerk
  253.          $0202:       Programmdatei: Ungültiger Pfad
  254.          $0203:       Programmdatei: Ungültiger Dateiname
  255.          $0204:       Programmdatei: Ungültiger Laufwerksbuchstabe
  256.          $0205:       Programmdatei: Pfad zu lang
  257.          $0206:       Programmdatei: Laufwerk nicht bereit
  258.          $0207:       Batchfile/COMMAND: COMMAND.COM nicht gefunden
  259.          $0208:       Fehler beim allozieren eines temporären Puffers
  260.  
  261.          $03xx:       DOS-Fehler-Code xx bei Aufruf von EXEC
  262.  
  263.          $0400:       Fehler beim allozieren der Umgebungsvariablenkopie
  264.  
  265.          $0500:       Auslagerung angefordert, aber prep_swap wurde nicht
  266.                        aufgerufen oder lieferte einen Fehler
  267.          $0501:       MCBs entsprechen nicht dem erwarteten Aufbau
  268.          $0502:       Fehler beim Auslagern
  269.  
  270.          $0600:      Redirection Syntaxfehler
  271.          $06xx:      DOS-Fehler xx bei Redirection
  272.    <}
  273.  
  274. procedure putenv (envvar: string);
  275. {  Adds a string to the environment. Note that the change to the
  276.    environment is valid for an exec'ed process only, and only if you
  277.    set the 'newenv' parameter in do_exec to TRUE. }
  278.  
  279.  
  280. function envcount: integer;
  281. function envstr (index: integer): string;
  282. function getenv (envvar: string): string;
  283.  
  284. { Replacement functions for the environment handling functions in the
  285.   DOS unit. All three functions work exactly like their DOS-unit
  286.   counterparts, except that they recognize the changes to the child
  287.   environment produced by 'putenv'. }
  288.  
  289.  
  290.  
  291. {===========================================================================}
  292.  
  293. Implementation
  294.  
  295. {>e
  296.    Define REDIRECT to support redirection.
  297.    CAUTION: The definition in 'spawn.asm' must match this definition!!
  298. <}
  299. {>d
  300.    Definieren Sie REDIRECT um Dateiumleitung zu untertützen.
  301.    ACHTUNG: Die Definition in 'spawn.asm' muß mit dieser Definition 
  302.    übereinstimmen!!
  303. <}
  304.  
  305. {$DEFINE REDIRECT}
  306.  
  307. const
  308.    swap_filename = '$$AAAAAA.AAA';
  309.  
  310.    {e internal flags for prep_swap }
  311.    {d interne Flags für prep_swap }
  312.  
  313.    CREAT_TEMP      = $0080;
  314.    DONT_SWAP_ENV   = $4000;
  315.  
  316.    ERR_COMSPEC     = -7;
  317.    ERR_NOMEM       = -8;
  318.  
  319.    spaces: set of #9..' ' = [#9, ' '];
  320.  
  321. type
  322.    stringptr = ^string;
  323.    stringarray = array [0..10000] of stringptr;
  324.    stringarrptr = ^stringarray;
  325.    bytearray = array [0..30000] of byte;
  326.    bytearrayptr = ^bytearray;
  327.  
  328. var
  329.    envptr: stringarrptr;   { Pointer to the changed environment }
  330.    envcnt: integer;        { Count of environment strings }
  331.    cmdpath: string;
  332.    cmdpars: string;
  333.    drive: string [3];
  334.    dir: string [67];
  335.    name: string [9];
  336.    ext: string [5];
  337.  
  338.  
  339. {$L spawnp}
  340. function do_spawn (swapping: integer;
  341.                    var xeqfn; var cmdtail; envlen: word;
  342.                    var env
  343. {$IFDEF REDIRECT}
  344.                    ;stdin: pstring; stdout: pstring; stderr: pstring
  345. {$ENDIF}
  346.                    ): integer; external;
  347.  
  348. function prep_swap (method: integer; var swapfn): integer; external;
  349.  
  350. { Environment routines }
  351.  
  352. function envcount: integer;
  353.  
  354.    { Returns count of strings in environment. }
  355.  
  356.    var
  357.       cnt: integer;
  358.    begin
  359.    if envptr = nil { If not yet changed }
  360.       then envcount := dos.envcount
  361.       else envcount := envcnt;
  362.    end;
  363.  
  364.  
  365. function envstr (index: integer): string;
  366.  
  367.    { Returns environment string 'index' }
  368.  
  369.    begin
  370.    if envptr = nil { If not yet changed }
  371.       then envstr := dos.envstr (index)
  372.       else if (index <= 0) or (index >= envcnt)
  373.       then envstr := ''
  374.       else if envptr^ [index - 1] = nil
  375.       then envstr := ''
  376.       else envstr := envptr^ [index - 1]^;
  377.    end;
  378.  
  379.  
  380. function name_eq (var n1, n2: string): boolean;
  381.  
  382.    { Compares search string 'n1' with environment string 'n2'.
  383.      Case is insignificant. }
  384.  
  385.    var
  386.       i: integer;
  387.       eq: boolean;
  388.    begin
  389.    i := 1;
  390.    eq := false;
  391.    while (i <= length (n1)) and (i <= length (n2)) and
  392.          (upcase (n1 [i]) = upcase (n2 [i])) do
  393.       i := i + 1;
  394.    name_eq := (i > length (n1)) and (i <= length (n2)) and (n2 [i] = '=');
  395.    end;
  396.  
  397.  
  398. function searchenv (var str: string): integer;
  399.  
  400.    { Search for environment string, returns index in 'envptr' array.
  401.      Assumes 'envptr' is not NIL. }
  402.  
  403.    var
  404.       idx: integer;
  405.       found: boolean;
  406.    begin
  407.    idx := 0;
  408.    found := false;
  409.  
  410.    while (idx < envcnt) and not found do
  411.       begin
  412.       if envptr^ [idx] <> nil
  413.          then found := name_eq (str, envptr^ [idx]^);
  414.       idx := idx + 1;
  415.       end;
  416.    if not found
  417.       then searchenv := -1
  418.       else searchenv := idx - 1;
  419.    end;
  420.  
  421.  
  422. function getenv (envvar: string): string;
  423.  
  424.    { Returns value of environment string specified by name. }
  425.  
  426.    var
  427.       strp: stringptr;
  428.       eq: integer;
  429.    begin
  430.    if envptr = nil { If not yet changed }
  431.       then getenv := dos.getenv (envvar)
  432.       else begin
  433.       eq := searchenv (envvar);
  434.       if eq < 0
  435.          then getenv := ''
  436.          else begin
  437.          strp := envptr^ [eq];
  438.          eq := pos ('=', strp^);
  439.          getenv := copy (strp^, eq + 1, length (strp^) - eq);
  440.          end;
  441.       end;
  442.    end;
  443.  
  444.  
  445. procedure init_envptr;
  446.  
  447.    { Initialise 'envptr' array. Called when 'putenv' is used for the
  448.      first time. Copies all environment strings into heap storage,
  449.      and builds an array of pointers to this strings. }
  450.  
  451.    var
  452.       i: integer;
  453.       str: string [255];
  454.    begin
  455.    envcnt := dos.envcount;
  456.    getmem (envptr, envcnt * sizeof (stringptr));
  457.    if envptr = nil
  458.       then exit;
  459.    for i := 0 to envcnt - 1 do
  460.       begin
  461.       str := dos.envstr (i + 1);
  462.       getmem (envptr^ [i], length (str) + 1);
  463.       if envptr^ [i] <> nil
  464.          then envptr^ [i]^ := str;
  465.       end;
  466.    end;
  467.  
  468.  
  469. procedure putenv (envvar: string);
  470.  
  471.    { Adds the string 'envvar' to the environment, or changes the
  472.      environment string if the name is already present. }
  473.  
  474.    var
  475.       idx, eq: integer;
  476.       help: stringarrptr;
  477.    begin
  478.    if envptr = nil
  479.       then init_envptr;
  480.    if envptr = nil
  481.       then exit;
  482.  
  483.    eq := pos ('=', envvar);
  484.    if eq = 0
  485.       then exit;
  486.    for idx := 1 to eq do
  487.       envvar [idx] := upcase (envvar [idx]);
  488.  
  489.    idx := searchenv (envvar);
  490.    if idx >= 0
  491.       then begin
  492.       freemem (envptr^ [idx], length (envptr^ [idx]^) + 1);
  493.  
  494.       if eq >= length (envvar)
  495.          then envptr^ [idx] := nil
  496.          else begin
  497.          getmem (envptr^ [idx], length (envvar) + 1);
  498.          if envptr^ [idx] <> nil
  499.             then envptr^ [idx]^ := envvar;
  500.          end;
  501.       end
  502.       else if eq < length (envvar)
  503.       then begin
  504.       getmem (help, (envcnt + 1) * sizeof (stringptr));
  505.       if help = nil
  506.          then exit;
  507.       move (envptr^, help^, envcnt * sizeof (stringptr));
  508.       freemem (envptr, envcnt * sizeof (stringptr));
  509.       envptr := help;
  510.       getmem (envptr^ [envcnt], length (envvar) + 1);
  511.       if envptr^ [envcnt] <> nil
  512.          then envptr^ [envcnt]^ := envvar;
  513.       envcnt := envcnt + 1;
  514.       end;
  515.    end;
  516.  
  517.  
  518.  
  519. { Routines to search for files }
  520.  
  521. function tryext (var fn: string): integer;
  522.  
  523.    { Try '.COM', '.EXE', and '.BAT' on current filename, modify filename if found. }
  524.  
  525.    var
  526.       nfn: filename;
  527.       ok: boolean;
  528.    begin
  529.    tryext := 1;
  530.    nfn := fn + '.COM';
  531.    ok := exists (nfn);
  532.    if not ok
  533.       then begin
  534.       nfn := fn + '.EXE';
  535.       ok := exists (nfn);
  536.       end;
  537.    if not ok
  538.       then begin
  539.       tryext := 2;
  540.       nfn := fn + '.BAT';
  541.       ok := exists (nfn);
  542.       end;
  543.    if not ok
  544.       then tryext := 0
  545.       else fn := nfn;
  546.    end;
  547.  
  548.  
  549. function findfile (var fn: string): integer;
  550.  
  551.    { Try to find the file 'fn' in the current path. Modifies the filename
  552.      accordingly. }
  553.  
  554.    var
  555.       path: string;
  556.       i, j: integer;
  557.       hasext, found, check: integer;
  558.    begin
  559.    if fn = ''
  560.       then begin
  561.       if cmdpath = ''
  562.          then findfile := ERR_COMSPEC
  563.          else findfile := 3;
  564.       exit;
  565.       end;
  566.  
  567.    check := checkpath (fn, drive, dir, name, ext, fn);
  568.    if check < 0
  569.       then begin
  570.       findfile := check;
  571.       exit;
  572.       end;
  573.  
  574.    if ((check and HAS_WILD) <> 0) or ((check and HAS_FNAME) = 0)
  575.       then begin
  576.       findfile := ERR_FNAME;
  577.       exit;
  578.       end;
  579.  
  580.    if (check and HAS_EXT) <> 0
  581.       then begin
  582.       for i := 1 to length (ext) do
  583.          ext [i] := upcase (ext [i]);
  584.       if ext = '.BAT'
  585.          then hasext := 2
  586.          else hasext := 1;
  587.       end
  588.       else hasext := 0;
  589.  
  590.    if hasext <> 0
  591.       then begin
  592.       if (check and FILE_EXISTS) <> 0
  593.          then found := hasext
  594.          else found := 0;
  595.       end
  596.       else found := tryext (fn);
  597.  
  598.    if (found <> 0) or ((check and (HAS_PATH or HAS_DRIVE)) <> 0)
  599.       then begin
  600.       findfile := found;
  601.       exit;
  602.       end;
  603.  
  604.    path := getenv ('PATH');
  605.    i := 1;
  606.    while (found = 0) and (i <= length (path)) do
  607.       begin
  608.       j := 0;
  609.       while (path [i] <> ';') and (i <= length (path)) do
  610.          begin
  611.          j := j + 1;
  612.          fn [j] := path [i];
  613.          i := i + 1;
  614.          end;
  615.       i := i + 1;
  616.       if (j > 0)
  617.          then begin
  618.          if not (fn [j] in ['\', '/'])
  619.             then begin
  620.             j := j + 1;
  621.             fn [j] := '\';
  622.             end;
  623.          fn [0] := chr (j);
  624.          fn := fn + name + ext;
  625.          check := checkpath (fn, drive, dir, name, ext, fn);
  626.          if hasext <> 0
  627.             then begin
  628.             if (check and FILE_EXISTS) <> 0
  629.                then found := hasext
  630.                else found := 0;
  631.             end
  632.             else found := tryext (fn);
  633.          end;
  634.       end;
  635.    findfile := found;
  636.    end; { findfile }
  637.  
  638.  
  639. {>e 
  640.    Get name and path of the command processor via the COMSPEC
  641.    environmnt variable. Any parameters after the program name
  642.    are copied and inserted into the command line.
  643. <}
  644. {>d
  645.    Namen und Pfad des Kommandoprozessors über die COMSPEC-Umgebungs-
  646.    Variable bestimmen. Parameter nach dem Programmnamen werden kopiert
  647.    und in die Kommandozeile eingefügt.
  648. <}
  649.  
  650. procedure getcmdpath;
  651.    var
  652.       i, found: integer;
  653.    begin
  654.    if length (cmdpath) > 0
  655.       then exit;
  656.    cmdpath := getenv ('COMSPEC');
  657.    cmdpars := '';
  658.    found := 0;
  659.  
  660.    if cmdpath <> ''
  661.       then begin
  662.       i := 1;
  663.       while (i <= length (cmdpath)) and (cmdpath [i] in spaces) do
  664.          inc (i);
  665.       if i > 1
  666.          then begin
  667.          cmdpath := copy (cmdpath, i, 255);
  668.          i := 1;
  669.          end;
  670.  
  671.       i := pos (';,=+/"[]|<> '#9, cmdpath);
  672.       if i <> 0
  673.          then begin
  674.          cmdpars := copy (cmdpath, i, 128);
  675.          cmdpath [0] := chr (i - 1);
  676.          i := 1;
  677.          while (i <= length (cmdpars)) and (cmdpars [i] in spaces) do
  678.             inc (i);
  679.          if i > 1
  680.             then cmdpars := copy (cmdpars, i, 128);
  681.          if cmdpars <> ''
  682.             then cmdpars := cmdpars + ' ';
  683.          end;
  684.       found := findfile (cmdpath);
  685.       end;
  686.  
  687.    if found = 0
  688.       then begin
  689.       cmdpath := 'COMMAND.COM';
  690.       cmdpars := '';
  691.       found := findfile (cmdpath);
  692.       if found = 0
  693.          then cmdpath := '';
  694.       end;
  695.    end;
  696.  
  697.  
  698. function tempdir (var outfn: filename): boolean;
  699.  
  700.    { Set temporary file path.
  701.      Read "TMP/TEMP" environment. If empty or invalid, clear path.
  702.      If TEMP is drive or drive+backslash only, return TEMP.
  703.      Otherwise check if given path is a valid directory.
  704.    }
  705.    var
  706.       stmp: array [0..3] of filename;
  707.       i, res: integer;
  708.  
  709.    begin
  710.    stmp [0] := getenv ('TMP');
  711.    stmp [1] := getenv ('TEMP');
  712.    stmp [2] := '.\';
  713.    stmp [3] := '\';
  714.  
  715.    for i := 0 to 3 do
  716.       if length (stmp [i]) <> 0
  717.          then begin
  718.          outfn := stmp [i];
  719.          res := checkpath (outfn, drive, dir, name, ext, outfn);
  720.          if (res > 0) and ((res and IS_DIR) <> 0) and ((res and IS_READ_ONLY) = 0)
  721.             then begin
  722.             tempdir := true;
  723.             exit;
  724.             end;
  725.          end;
  726.    tempdir := false;
  727.    end;
  728.  
  729.  
  730. {$IFDEF REDIRECT}
  731.  
  732. function parse_redirect (var par: string; idx: integer;
  733.                          var stdin, stdout, stderr: pstring): boolean;
  734.    var
  735.       ch: char;
  736.       fnp: pstring;
  737.       fn: string;
  738.       app, i, fne: integer;
  739.  
  740.    begin
  741.    i := idx;
  742.    par [length (par) + 1] := #0;
  743.  
  744.    repeat
  745.       app := 0;
  746.       ch := par [i];
  747.       i := i + 1;
  748.       if ch <> '<'
  749.          then begin
  750.          if par [i] = '&'
  751.             then begin
  752.             ch := '&';
  753.             inc (i);
  754.             end;
  755.          if par [i] = '>'
  756.             then begin
  757.             app := 1;
  758.             inc (i);
  759.             end;
  760.          end;
  761.  
  762.       while (i <= length (par)) and (par [i] in spaces) do
  763.          inc (i);
  764.       fn := copy (par, i, 255);
  765.       fne := pos (';,=+/"[]|<> '#9, fn);
  766.       if fne = 0
  767.          then fne := length (fn) + 1;
  768.       i := i + fne - 1;
  769.       fn [0] := chr (fne - 1);
  770.       if (fne = 0) or (length (fn) = 0)
  771.          then begin
  772.          parse_redirect := false;
  773.          exit;
  774.          end;
  775.       
  776.       getmem (fnp, length (fn) + app + 2);
  777.       if fnp = NIL
  778.          then begin
  779.          parse_redirect := false;
  780.          exit;
  781.          end;
  782.       if app <> 0
  783.          then fnp^ := '>' + fn
  784.          else fnp^ := fn;
  785.       fnp^ [length (fnp^) + 1] := #0;
  786.  
  787.       case ch of
  788.          '<':  if stdin <> NIL
  789.                   then begin
  790.                   parse_redirect := false;
  791.                   exit;
  792.                   end
  793.                else stdin := fnp;
  794.  
  795.          '>':  if stdout <> NIL
  796.                   then begin
  797.                   parse_redirect := false;
  798.                   exit;
  799.                   end
  800.                else stdout := fnp;
  801.  
  802.          '&':  if stderr <> NIL
  803.                   then begin
  804.                   parse_redirect := false;
  805.                   exit;
  806.                   end
  807.                else stderr := fnp;
  808.          end;
  809.  
  810.       while (i <= length (par)) and (par [i] in spaces) do
  811.          inc (i);
  812.  
  813.    until (i > length (par)) or (par [i] <> '>') and (par [i] <> '<');
  814.  
  815.    par [idx] := #0;
  816.    par [0] := chr (idx - 1);
  817.    parse_redirect := true;
  818.    end;
  819.  
  820. {$ENDIF}
  821.  
  822.  
  823. function do_exec (xfn: string; pars: string; spawn: integer;
  824.                   needed: word; newenv: boolean): integer;
  825.    label
  826.       exit;
  827.    var
  828.       swapfn: filename;
  829.       avail: word;
  830.       regs: registers;
  831.       envlen, einx: word;
  832.       idx, len, rc: integer;
  833.       envp: bytearrayptr;
  834.       swapping: integer;
  835. {$IFDEF REDIRECT}
  836.       stdin, stdout, stderr: pstring;
  837. {$ENDIF}
  838.    begin
  839. {$IFDEF REDIRECT}
  840.    stdin := NIL; stdout := NIL; stderr := NIL;
  841. {$ENDIF}
  842.  
  843.    getcmdpath;
  844.    envlen := 0;
  845.  
  846.    { First, check if the file to execute exists. }
  847.  
  848.    rc := findfile (xfn);
  849.    if rc <= 0
  850.       then begin
  851.       do_exec := RC_NOFILE or -rc;
  852.       goto exit;
  853.       end;
  854.  
  855.    if rc > 1   { COMMAND.COM or Batch file }
  856.       then begin
  857.       if length (cmdpath) = 0
  858.          then begin
  859.          do_exec := RC_NOFILE or -ERR_COMSPEC;
  860.          goto exit;
  861.          end;
  862.  
  863.       if rc = 2
  864.          then pars := cmdpars + '/c ' + xfn + ' ' + pars
  865.          else pars := cmdpars + pars;
  866.       xfn := cmdpath;
  867.       end;
  868.  
  869. {$IFDEF REDIRECT}
  870.    idx := pos ('<', pars);
  871.    len := pos ('>', pars);
  872.    if len > idx
  873.       then idx := len;
  874.    if idx > 0
  875.       then if not parse_redirect (pars, idx, stdin, stdout, stderr)
  876.          then begin
  877.          do_exec := RC_REDIRERR;
  878.          goto exit;
  879.          end;
  880. {$ENDIF}
  881.  
  882.    { Now create a copy of the environment if the user wants it, and
  883.      if the environment has been changed. }
  884.  
  885.    if newenv and (envptr <> nil)
  886.       then begin
  887.       for idx := 0 to envcnt - 1 do
  888.          envlen := envlen + length (envptr^ [idx]^) + 1;
  889.       if envlen > 0
  890.          then begin
  891.          envlen := envlen + 1;
  892.          getmem (envp, envlen);
  893.          if envp = nil
  894.             then begin
  895.             do_exec := RC_ENVERR;
  896.             goto exit;
  897.             end;
  898.          einx := 0;
  899.          for idx := 0 to envcnt - 1 do
  900.             begin
  901.             len := length (envptr^ [idx]^);
  902.             move (envptr^ [idx]^ [1], envp^ [einx], len);
  903.             envp^ [einx + len] := 0;
  904.             einx := einx + len + 1;
  905.             end;
  906.          envp^ [einx] := 0;
  907.          end;
  908.       end;
  909.  
  910.    if spawn = 0
  911.       then swapping := -1
  912.       else begin
  913.  
  914.       { Determine amount of free memory }
  915.       with regs do
  916.          begin
  917.          ax := $4800;
  918.          bx := $ffff;
  919.          msdos (regs);
  920.          avail := regs.bx;
  921.          end;
  922.  
  923.       { No swapping if available memory > needed }
  924.  
  925.       if needed < avail
  926.          then swapping := 0
  927.          else begin
  928.  
  929.          { Swapping necessary, use 'TMP' or 'TEMP' environment variable
  930.            to determine swap file path if defined. }
  931.  
  932.          swapping := spawn;
  933.          if (spawn and USE_FILE) <> 0
  934.             then begin
  935.             if not tempdir (swapfn)
  936.                then begin
  937.                spawn := spawn xor USE_FILE;
  938.                swapping := spawn;
  939.                end
  940.                else begin
  941.                if (dosversion and $ff) >= 3
  942.                   then swapping := swapping or CREAT_TEMP
  943.                   else begin
  944.                   swapfn := swapfn + swap_filename;
  945.                   len := length (swapfn);
  946.                   while exists (swapfn) do
  947.                      begin
  948.                       if (swapfn [len] >= 'Z')
  949.                         then len := len - 1;
  950.                       if (swapfn [len] = '.')
  951.                         then len := len - 1;
  952.                       swapfn [len] := succ (swapfn [len]);
  953.                       end;
  954.                   end;
  955.                swapfn [length (swapfn) + 1] := #0;
  956.                end;
  957.             end;
  958.          end;
  959.       end;
  960.  
  961.    { All set up, ready to go. }
  962.  
  963.    if swapping > 0
  964.       then begin
  965.       if envlen = 0
  966.          then swapping := swapping or DONT_SWAP_ENV;
  967.  
  968.       rc := prep_swap (swapping, swapfn);
  969.       if rc < 0
  970.          then begin
  971.          do_exec := RC_PREPERR or -rc;
  972.          goto exit;
  973.          end;
  974.       end;
  975.  
  976.    xfn [length (xfn) + 1] := #0;
  977.    pars [length (pars) + 1] := #0;
  978.    swapvectors;
  979. {$IFDEF REDIRECT}
  980.    do_exec := do_spawn (swapping, xfn, pars, envlen, envp^, stdin, stdout, stderr);
  981. {$ELSE}
  982.    do_exec := do_spawn (swapping, xfn, pars, envlen, envp^);
  983. {$ENDIF}
  984.    swapvectors;
  985.  
  986.    { Free the environment buffer if it was allocated. }
  987.  
  988. exit:
  989.    if envlen > 0
  990.       then freemem (envp, envlen);
  991. {$IFDEF REDIRECT}
  992.    if stdin <> NIL
  993.       then freemem (stdin, length (stdin^) + 2);
  994.    if stdout <> NIL
  995.       then freemem (stdout, length (stdout^) + 2);
  996.    if stderr <> NIL
  997.       then freemem (stderr, length (stderr^) + 2);
  998. {$ENDIF}
  999.    end;
  1000.  
  1001.  
  1002. { Initialisation for environment processing }
  1003.  
  1004. Begin
  1005. envptr := nil;
  1006. envcnt := 0;
  1007. cmdpath := '';
  1008. End.
  1009.  
  1010. 
  1011.